#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "device_auth.h"
#include "securec.h"


#define PORT 10001
#define LOGI printf
#define LOGE printf
#define LOGW printf
#define TEST_APP_NAME "com.huawei.security"
static int clientfd = -1;
static int listenfd = -1;
int g_finishBind = 0;
DataChangeListener listenerCtx = {0};
const DeviceGroupManager *g_gmInst = 0;
static char requestfamot[] = \
"{\"confirmation\":%lld,\"pinCode\":\"000000\",\"deviceId\":\"\
ABCDEF00ABCDEF00ABCDEF00ABCDEF00ABCDEF00ABCDEF00ABCDEF00ABCDEF11\"}";

void printbuf(char *buf, int len)
{
#define OUTBUF 250
#define PRINTBUF 200
#define PRINTMAXCNT 10
    char out[OUTBUF] = {0};
    int outlen = 0;
    int cnt = PRINTMAXCNT;
    LOGI("-----begin print len %d\r\n", len);
    while(cnt) {
        if (outlen < len) {
            memcpy(out, buf + outlen, PRINTBUF);
            printf("%s\r\n", out);
            outlen += PRINTBUF;
        }
        cnt--;
    }
    LOGI("------end print len %d\r\n", len);
}
static bool OnTransmitNet(int64_t requestId, const uint8_t *data, uint32_t dataLen)
{
    int32_t cnt;
    LOGI("------------------OnTransmit------------------\r\n");
    LOGI("|  RequestId: %-30lld  |\r\n", requestId);
    printbuf((char *)data, dataLen + 1);
    LOGI("------------------OnTransmit------------------\r\n");
    cnt = send(clientfd, data, dataLen, 0);
    LOGI("send size %d, process size %d\r\n", dataLen, cnt);
    return true;
}

static void OnSessionKeyReturned(int64_t requestId, const uint8_t *sessionKey, uint32_t sessionKeyLen)
{
    LOGI("------------------OnSessionKeyReturned------------------\r\n");
    LOGI("|  RequestId: %-40lld  |\r\n", requestId);
    LOGI("|  SessionKeyLen: %-36d  |\r\n", sessionKeyLen);
    LOGI("------------------OnSessionKeyReturned------------------\r\n");
}

static void OnFinish(int64_t requestId, int operationCode, const char *authReturn)
{
    LOGI("------------------OnFinish------------------\r\n");
    LOGI("|  RequestId: %-27lld  |\r\n", requestId);
    LOGI("|  OperationCode: %-23d  |\r\n", operationCode);
    LOGI("|  ReturnData: %-26s  |\r\n", authReturn);
    LOGI("------------------OnFinish------------------\r\n");
    if (operationCode == 1 || operationCode == 0 ) {
        return;
    }
    close(clientfd);
    close(listenfd);
    g_finishBind = 1;
}

static void OnError(int64_t requestId, int operationCode, int errorCode, const char *errorReturn)
{
    LOGI("-------------------OnError------------------\r\n");
    LOGI("|  RequestId: %-27lld  |\r\n", requestId);
    LOGI("|  OperationCode: %-23d  |\r\n", operationCode);
    LOGI("|  ErrorCode: %-27d  |\r\n", errorCode);
    LOGI("-------------------OnError------------------\r\n");
}

static char *OnBindRequestController(int64_t requestId, int operationCode, const char *reqParams)
{
#define REQUSTBUF 300
#define CONFIRMVAL 0x80000006
    LOGI("------------------OnRequest------------------\r\n");
    LOGI("|  RequestId: %-29lld  |\r\n", requestId);
    LOGI("|  OperationCode: %-25d  |\r\n", operationCode);
    LOGI("|  reqParams: %s  |\r\n", (char *)reqParams);
    LOGI("------------------OnRequest------------------\r\n");
    char *buf = calloc(1, REQUSTBUF);
    int64_t val = CONFIRMVAL;
    (void)sprintf_s(buf, REQUSTBUF, requestfamot, val);
    LOGI("buf = %s\r\n", buf);
    return buf;

}

static const DeviceGroupManager *registerCallbackController(DeviceAuthCallback *callback)
{
    callback->onRequest = OnBindRequestController;
    callback->onError = OnError;
    callback->onFinish = OnFinish;
    callback->onSessionKeyReturned = OnSessionKeyReturned;
    callback->onTransmit = OnTransmitNet;
    const DeviceGroupManager *gm = GetGmInstance();
    gm->regCallback(TEST_APP_NAME, callback);
    return gm;
}


void onGroupCreated(const char *groupInfo)
{
    if (groupInfo == NULL) {
        LOGW("not expected");
        return;
    }
    LOGI("%s", groupInfo);
}
void onGroupDeleted(const char *groupInfo)
{
    if (groupInfo == NULL) {
        LOGW("not expected");
        return;
    }
    LOGI("%s", groupInfo);
}

int BindLocalSocket(int port)
{
    int rc;
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        LOGI("fd=%d\r\n", fd);
        return -1;
    }
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
    if (rc < 0) {
        LOGI("bind error fd=%d,rc=%d", fd, rc);
        return -1;
    }
    return fd;
}
static void TestControllerInServer(void)
{
#define RECVBUF 2048
#define REQESTID 1234567890000000
    int ret;
    char buf[RECVBUF] = {0};
    int count;

    listenfd = BindLocalSocket(PORT);
    if (listenfd < 0) {
        LOGI("listen %d port err %d \r\n", PORT, listenfd);
        return;
    }
    ret = listen(listenfd, 1);
    if(ret == -1) {
        LOGI("listen failed");
        close(listenfd);
        return;
    }
    LOGI("listen port  %d，wait for new connection... ret %d\r\n",  PORT, listenfd);

    struct sockaddr_in fromaddr;
    socklen_t len = sizeof(fromaddr);
    LOGI("wait new connection...\r\n");
    while (1) {
        if (g_finishBind) {
            LOGI( "server bind finish");
            LOGI("finish quit..\r\n");
            break;
        }
        if (clientfd < 0) {
            clientfd = accept(listenfd, (struct sockaddr *)&fromaddr, &len);
            if(clientfd == -1) {
                printf("accept failed\r\n");
                sleep(1);
                continue;
            }
        }

        memset(buf, 0, sizeof(buf));
        count = recv(clientfd, buf, sizeof(buf), 0);
        if (count < 0) {
            printf("recv fail");
            return;
        }
        if (count > 0) {
            LOGI("wdf recv date len %d\r\n", count);
            printbuf(buf, count);
            g_gmInst->processData(REQESTID, (uint8_t *)buf, (uint32_t)(count));
        }
        sleep(1);
    }
}


void devauth_test_bind()
{
    InitDeviceAuthService();
    DeviceAuthCallback callback;
    g_gmInst = registerCallbackController(&callback);
    if (g_gmInst == NULL) {
        LOGI("init module instance failed\r\n");
        return;
    }

    listenerCtx.onGroupCreated = onGroupCreated;
    listenerCtx.onGroupDeleted = onGroupDeleted;
    g_gmInst->regDataChangeListener(TEST_APP_NAME, &listenerCtx);
    TestControllerInServer();
}

int main(int argc, char **argv)
{
    LOGI("server wating bind  del /data/data/deviceauth/hcgroup.dat before \n");
    devauth_test_bind();
    LOGI("bind ok");
    return 0;
}